Skip to content

feat(display-controller): round-robin between simultaneous live-priority games#372

Merged
ChuckBuilds merged 1 commit into
ChuckBuilds:mainfrom
rpierce99:feat/live-priority-round-robin
Jun 15, 2026
Merged

feat(display-controller): round-robin between simultaneous live-priority games#372
ChuckBuilds merged 1 commit into
ChuckBuilds:mainfrom
rpierce99:feat/live-priority-round-robin

Conversation

@rpierce99

@rpierce99 rpierce99 commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Problem

_check_live_priority() returns the first plugin in registration order that has live priority + live content, then stops. The post-dwell hold (should_rotate = False) keeps the carousel on that plugin while it stays live. So when two plugins report live content at the same time — e.g. a baseball game and a soccer match — the board pins to whichever registers first and never shows the other until the first game ends. There is no mechanism to alternate between simultaneously-live games.

Fix

Make the live-priority selection round-robin instead of first-match-wins, without changing the hold semantics:

  • New _collect_live_modes() returns all currently-live modes across every plugin (deduped, in registration order). For each plugin with live priority + content it takes the specific modes from get_live_modes() (only those actually registered), falling back to the scanned *_live mode name. A plugin registered under several mode keys (the sports plugins register one per league) contributes each live mode once.
  • _check_live_priority(advance=False):
    • advance=True (the main rotation call site) returns the live mode after the one currently shown, using current_display_mode as the cursor — so each dwell advances to the next live game and they take turns.
    • advance=False (default; used by the Vegas coordinator and the vegas-active check) is a non-advancing peek that returns the live mode already on screen if it is still live, else the first. These only need to know whether any game is live and must not spin the cursor.

should_rotate and _apply_live_priority are unchanged: a single live game still holds in place, ambient modes stay suppressed while any game is live, and _apply_live_priority already switches to whatever mode the checker returns and resumes rotation when it returns None. Using current_display_mode as the cursor keeps selection correct as games start and end — there is no separate index to keep in sync.

Testing

New tests in TestDisplayControllerLivePriority:

  • round-robin alternates between two simultaneous live games;
  • a single live game holds without flipping;
  • the non-advancing peek does not rotate (Vegas safety);
  • _collect_live_modes dedupes a multi-mode plugin and skips no-content plugins;
  • fallback to the *_live mode name when get_live_modes is unhelpful;
  • no live content returns None.

All existing live-priority tests still pass. Verified end-to-end in the RGBMatrixEmulator with two simultaneous test-mode live games (MLB + MiLB): the controller alternated between them each dwell, with ambient modes suppressed — previously it pinned to the first and the second never showed.

Summary by CodeRabbit

Release Notes

  • New Features

    • When multiple games are simultaneously live, the system now cycles through them in turn rather than repeatedly displaying the first available game.
    • Improved detection of plugins with active live content, including more accurate discovery of live modes.
  • Tests

    • Added comprehensive test coverage for live-priority behavior with multiple simultaneous games, including single-game stability and content detection scenarios.

…ity games

_check_live_priority() was stateless first-match-wins: it returned the
first plugin in registration order with live content, and the post-dwell
hold pinned the carousel to it, so when two games were live at once (e.g.
a baseball game and a soccer match) the second never showed until the
first ended.

Add _collect_live_modes() (all currently-live modes, deduped, in
registration order) and give _check_live_priority an 'advance' flag. The
main rotation calls it with advance=True, which returns the live mode
after the one currently shown -- using current_display_mode as the cursor
-- so each dwell advances to the next live game and they take turns. The
Vegas coordinator and the vegas-active check keep the default
non-advancing peek (advance=False), so they only report whether any game
is live without spinning the cursor. should_rotate and _apply_live_priority
are unchanged; a single live game still holds as before.

Adds regression tests to TestDisplayControllerLivePriority.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Refactors DisplayController live-priority handling by extracting a new _collect_live_modes() method that scans plugins for live content and deduplicates mode keys, extending _check_live_priority() with an advance flag for round-robin switching, updating the run() loop to call advance=True, and adding six pytest cases covering all new behaviors.

Changes

Round-robin live-priority selection

Layer / File(s) Summary
_collect_live_modes() and _check_live_priority(advance) implementation
src/display_controller.py
_collect_live_modes() scans plugin_modes for plugins reporting both live priority and live content, resolves concrete mode keys via get_live_modes() or falls back to *_live naming, and deduplicates while preserving order. _check_live_priority() gains an advance flag: peek mode returns the current live mode if still live or the first live mode; advance mode returns the next live mode after the currently displayed one for round-robin switching.
run() loop wired to advance=True
src/display_controller.py
The live-priority interrupt path in the main loop is updated to call _check_live_priority(advance=True) when on-demand and WiFi-status interrupts are not active, rotating simultaneous live modes turn-by-turn.
TestDisplayControllerLivePriority test suite
test/test_display_controller.py
Adds _live_plugin() static helper and six pytest cases: deduplication across multi-mode plugins, round-robin alternation between two simultaneous live games, single-game hold stability, non-advancing peek does not rotate, no live content returns None, and fallback to mode_live name when get_live_modes() is unhelpful.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • ChuckBuilds/LEDMatrix#298: Modifies the same live-priority interrupt path in src/display_controller.py by changing how _check_live_priority() is invoked during the main display loop to preempt the active mode.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main feature: implementing round-robin selection between simultaneous live-priority games in the display controller.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 6 complexity · 0 duplication

Metric Results
Complexity 6
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/display_controller.py (1)

1543-1569: ⚡ Quick win

Consider adding type hints for clarity.

The method signature and return value would benefit from type annotations to make the contract explicit:

def _check_live_priority(self, advance: bool = False) -> Optional[str]:

This documents that advance is a boolean flag and the method returns either a mode name string or None.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/display_controller.py` around lines 1543 - 1569, The _check_live_priority
method lacks type hints for its parameters and return value, reducing code
clarity. Add type annotations to the method signature: annotate the advance
parameter as bool with its default value of False, and add a return type
annotation of Optional[str] since the method returns either a mode name string
or None. This documents the contract explicitly and improves IDE support and
code readability.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/display_controller.py`:
- Around line 1543-1569: The _check_live_priority method lacks type hints for
its parameters and return value, reducing code clarity. Add type annotations to
the method signature: annotate the advance parameter as bool with its default
value of False, and add a return type annotation of Optional[str] since the
method returns either a mode name string or None. This documents the contract
explicitly and improves IDE support and code readability.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7ecf0095-c843-4113-aaf3-5f9ee84c2cf3

📥 Commits

Reviewing files that changed from the base of the PR and between d22d0a3 and 29ddbb0.

📒 Files selected for processing (2)
  • src/display_controller.py
  • test/test_display_controller.py

@ChuckBuilds ChuckBuilds merged commit d297dd6 into ChuckBuilds:main Jun 15, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants